--[[
ViewManager.lua
Created by DX on 17-3-31.
Fixed by on 17-8-17.

view的各种属性
viewType:view类型
layerType:渲染层级

--属性参数优先级,取优先级最大的属性参数起作用
param中参数 > viewModule文件中设置的属性(或在initView方法前设置的属性) > 编辑器中设置的属性

说明:
1.VIEWM.showView(VIEW.NAME): 显示界面，不带fromViewName和fromViewParam
2.this.showView(VIEW.NAME): 带fromViewName和fromViewParam，该方法只能在对应界面的view脚本中使用
3.this.showView(VIEW.TEST, {childViewName = VIEW.TEST1})
  this.showView(VIEW.TEST, {childViewName = VIEW.TEST2})
  类似活动界面分页类型的显示方式，前面为主面板，后面会page，
  使用vieweditor生成child界面时，只需要选择viewType=PAGE 和 选择父文件路径


Back(): 一般只在VIEW_TYPE.PANEL类型界面中使用，返回前一级面板
Close(): 直接退出当前界面，一般DIALOG类型面板使用
--]]

module("VIEWM",package.seeall)
require 'Common.utils_table'
require "Common.view_constants"
require "Common.view_def"

HIERARCHYSTACK = {}
HIERARCHY = {}
CLOSING_VIEWS = {}
UIRoot = UnityEngine.GameObject.Find("init/ScreenCanvas2D").transform
GuiCamera = UnityEngine.GameObject.Find("init/ScreenCanvas2D/Camera").transform
GuiCameraComponent = GuiCamera:GetComponent('Camera')
EventSystem = UnityEngine.GameObject.Find('init/EventSystem'):GetComponent('EventSystem')
allVisibleViews = {}
CanvasSize = UIRoot:GetComponent('RectTransform').sizeDelta
CanvasScale = UIRoot:GetComponent('RectTransform').localScale

ViewTransitionQueue = {}
ViewTransitionType = {
    Show = 1,
    Hide = 2,
}

local isDisposed = false

--get view moudle
function getView(viewName)
    if viewName == nil then return nil end

    local view = VIEW_MODULE[viewName]
    if view == nil then
        view = {}
        view.viewName = viewName
        if VIEW_CONFIG[view.viewName] then
            for k,v in pairs(VIEW_CONFIG[view.viewName]) do
                if view[k] == nil then
                    view[k] = v
                end
            end
        end

        VIEW_MODULE[viewName] = view;
    end

    if(view.viewType == nil) then
        print('无法定位到界面配置KEY，无法排序。请检查viewName是否正确! ViewName:',view.viewName)
    end

    return view
end

function isViewShow(viewName)
    return getView(viewName).panelView ~= nil
end

function getTopVisibleView()
    local res = nil
    for k, v in pairs(HIERARCHY) do
        local currentShowViewInType = v[#v]

        if currentShowViewInType and currentShowViewInType.visible then
            if currentShowViewInType.viewType == VIEW_TYPE.DIALOG or  currentShowViewInType.viewType == VIEW_TYPE.DIALOGA  then
                res = currentShowViewInType
            elseif  currentShowViewInType.viewType == VIEW_TYPE.PANEL then
                res = currentShowViewInType
            end
        end
    end
    return res
end

local ViewSequence = 1

local function CreatePanel(viewName)
	local parentView = getView(viewName)
	if parentView.panelView == nil then
        local view = getView(viewName)

        local abName = view.viewPath
        view.panelView = RESM.InstantiateAsset(abName)
        if not utils.IsAvailableGameObject(view.panelView) then           --解压完删了进度条
           return
        end
        view.sequence = ViewSequence
        ViewSequence = ViewSequence + 1
	end

    parentView.panelView:SetActive(true);
    parentView.panelView.layer = LayerMask.NameToLayer("UI");
    parentView.panelView.transform:SetParent(UIRoot,false);
    parentView.panelView.transform.localScale = Vector3.one;
   -- parentView.panelView.transform.localPosition = Vector3.zero;  --设置坐标适配有问题，位置不对改prefab
    parentView.panelView.name = viewName

    local luaBehaviour = parentView.panelView:GetComponent('LuaBehaviour')
    if luaBehaviour then
        parentView.View = luaBehaviour.LuaObject
        parentView.View.constantView = parentView.constantView
        luaBehaviour.LuaObject.viewName = parentView.viewName
        luaBehaviour.LuaObject.param = parentView.param
    end
end

local function showViewInternal(parentView,viewModel)
    if parentView.visible then
        return
    end

    local lastCo = parentView.transitionCo
    if lastCo then
        coroutine.stop(lastCo)
    end

    parentView.visible = true;
    CreatePanel(parentView.viewName)
    if  not parentView.View then
        return
    end
    if viewModel then
        parentView.View:Set_BindingContext(viewModel)
    end

    parentView.View:OnBeginShow()
    local appearTime = parentView.View:AppearAnimation()

    if appearTime ~= 0 then
        parentView.transitionCo = coroutine.start(function()
            coroutine.wait(appearTime)
            parentView.transitionCo = nil
        end)
    end

end

local function hideViewInternal(parentView, ignoreAnimation)
    if not parentView.visible or not parentView.View then
        return
    end

    local lastCo = parentView.transitionCo
    if lastCo then
        coroutine.stop(lastCo)
    end

    parentView.visible = false
    parentView.View:OnBeginHide()
    local disappearTime = ignoreAnimation and 0 or parentView.View:DisappearAnimation()
    if disappearTime == 0 then
        GameObject.Destroy(parentView.panelView);
        parentView.panelView = nil
        parentView.View = nil
        parentView.transitionCo = nil
    else
        parentView.transitionCo = coroutine.start(function()
            coroutine.wait(disappearTime)
            GameObject.Destroy(parentView.panelView);
            parentView.panelView = nil
            parentView.View = nil
            parentView.transitionCo = nil
        end)
    end
end

local function SortViews()
    allVisibleViews = {}

    local findVisibleViews = function(currentShowViewInType)
        if currentShowViewInType and currentShowViewInType.panelView ~= nil then
            table.insert(allVisibleViews, currentShowViewInType)
        end
    end
    
    for k, v in pairs(HIERARCHY) do
        findVisibleViews(v[#v])
    end

    for k, v in pairs(CLOSING_VIEWS) do
        findVisibleViews(v)
    end

    utils.sort(allVisibleViews, {function (a, b)
        if a.layerType > b.layerType then
            return 1
        elseif a.layerType == b.layerType then
            return 0
        else
            return -1
        end
    end, function(a, b)
        if a.sequence > b.sequence then
            return 1
        elseif a.sequence == b.sequence then
            return 0
        else
            return -1
        end
    end})

    --print('=================BEGIN=====================')
    --for _, view in ipairs(allVisibleViews) do
    --    print(view.viewName)
    --end
    --print('=================END=====================')

    for _, view in ipairs(allVisibleViews) do
        if utils.IsAvailableGameObject(view.panelView) then
            view.panelView.transform:SetAsLastSibling()
        end
    end
end

--判断此时是不是需要关闭渲染场景
local function CalculateHideScene()
    local hideScene = false
    for _, view in pairs(HIERARCHY) do
        if view.hideScene then
            hideScene = true
            break
        end
    end

    HideScene = hideScene
    EVTM.dispatch(EVENT.CAMERA_SWITCH, not HideScene)
end

local cos = {}
function showView(viewName, viewModel, ignoreHideViewAnimation)
    if isDisposed then
        return
    end

    coroutine.start(function()
            table.insert(cos, coroutine.running())
        local parentView = getView(viewName)

        if parentView.hideAllVisible then--把所有界面压入栈维护
        local hasAlreadyInHierarchy = false
            for _, hierarchy in pairs(HIERARCHYSTACK) do
                if hierarchy.name == viewName then
                    hasAlreadyInHierarchy = true
                    break
                end
            end
            if hasAlreadyInHierarchy then
                return
            end

            local storeHierarchy = {}
            local hideViews = {}
            for viewType, viewList in pairs(HIERARCHY) do
                storeHierarchy[viewType] = {}
                for i = #viewList, 1, -1 do--这里逆序压入，恢复记着也要逆序
                    local aView = viewList[i]
                    if aView.constantView == false then
                        table.insert(storeHierarchy[viewType], 1, aView)
                        table.remove(viewList, i)
                        table.insert(hideViews, aView)

                        hideViewInternal(aView, true)
                    end
                end
            end
            table.insert(HIERARCHYSTACK, {
                name = viewName,
                hierarchy = storeHierarchy
            })

            EVTM.dispatch(EVENT.SHOW_HIDE_ALL_VIEW)
        end

        if HIERARCHY[parentView.viewType] == nil then
            HIERARCHY[parentView.viewType] = {}
        end
        local viewStack = HIERARCHY[parentView.viewType]
        local lastShowViewInType = viewStack[#viewStack]

        local index = table.findByFunc(HIERARCHY[parentView.viewType], function(a)
            return a.viewName == viewName
        end)

        if index ~= -1 then
            table.remove(HIERARCHY[parentView.viewType], index)
        end
        table.insert(HIERARCHY[parentView.viewType], parentView)

        local currentShowViewInType = viewStack[#viewStack]
        if currentShowViewInType ~= lastShowViewInType then
            if lastShowViewInType then
                hideViewInternal(lastShowViewInType, ignoreHideViewAnimation)
            end

            if currentShowViewInType then
                showViewInternal(currentShowViewInType, viewModel)
            end

            SortViews()
            CalculateHideScene()

            local transitionCo = currentShowViewInType.transitionCo
            if transitionCo then
                coroutine.waitCo(transitionCo)
            end
        end
        table.remove_value(cos, coroutine.running())
    end)
end

--hide view
function hideView(viewName)
    if isDisposed then
        return
    end

    coroutine.start(function()
        table.insert(cos, coroutine.running())
        local parentView = getView(viewName)
        parentView.param = nil
        if parentView.hideAllVisible and parentView.visible then
            local hideViews = {}
            for _, viewList in pairs(HIERARCHY) do--1.清空之前HIERARCHY所有界面
                for i = #viewList, 1, -1 do
                    local aView = viewList[i]
                    if aView.constantView == false then
                        table.remove(viewList, i)
                        hideViewInternal(aView, true)
                        table.insert(hideViews, aView)
                    end
                end
            end

            local restoreHierarchy = nil--2.弹出上一个HIERARCHY快照
            if #HIERARCHYSTACK > 0 then
                restoreHierarchy = HIERARCHYSTACK[#HIERARCHYSTACK]
                table.remove(HIERARCHYSTACK, #HIERARCHYSTACK)
            end

            local showViews = {}
            if restoreHierarchy then --3.用上一个HIERARCHY快照恢复
                for viewType, viewList in pairs(restoreHierarchy.hierarchy) do
                    for i = 1, #viewList do
                        local aView = viewList[i]
                        table.insert(HIERARCHY[viewType], aView)

                        if i == #viewList then
                            if aView.transitionCo then
                                local transitionCo = aView.transitionCo
                                coroutine.waitCo(transitionCo)
                            end
                            showViewInternal(aView)
                            table.insert(showViews, aView)
                        end
                    end
                end
            end
            SortViews()
            CalculateHideScene()

            for _, view in pairs(showViews) do
                local transitionCo = view.transitionCo
                if transitionCo then
                    coroutine.waitCo(transitionCo)
                end
            end
        else --普通的顶界面方式
            if HIERARCHY[parentView.viewType] == nil then
                HIERARCHY[parentView.viewType] = {}
            end
            local viewStack = HIERARCHY[parentView.viewType]
            local lastShowViewInType = viewStack[#viewStack]

            local index = table.findByFunc(HIERARCHY[parentView.viewType], function(a)
                return a.viewName == viewName
            end)

            if index ~= -1 then
                table.remove(HIERARCHY[parentView.viewType], index)
                table.insert(CLOSING_VIEWS, parentView)
            else --之前不在界面堆栈中，关闭没意义
                return
            end

            viewStack = HIERARCHY[parentView.viewType]
            local currentShowViewInType = viewStack[#viewStack]

            if currentShowViewInType ~= lastShowViewInType then
                if lastShowViewInType then
                    hideViewInternal(lastShowViewInType)
                    local hideCo = lastShowViewInType.transitionCo
                    if hideCo then
                        coroutine.waitCo(hideCo)
                    end
                    table.remove_value(CLOSING_VIEWS, parentView)
                end

                if currentShowViewInType then
                    showViewInternal(currentShowViewInType)
                    SortViews()
                    CalculateHideScene()
                    local showCo = currentShowViewInType.transitionCo
                    if showCo then
                        coroutine.waitCo(showCo)
                    end
                end
            end
        end
        table.remove_value(cos, coroutine.running())
    end)
end

function ClearAllUI()
    for _, co in ipairs(cos) do
        coroutine.stop(co)
    end
    cos = {}

    for viewType, viewStack in pairs(HIERARCHY) do
        for i = #viewStack , 1, -1 do
            local view = viewStack[i]
            if not view.constantView then
                hideViewInternal(view, true)
                table.remove(viewStack, i)
            end
        end
    end

    HIERARCHYSTACK = {}
end

function Destroy()
    ClearAllUI()
    local childCount = UIRoot.childCount
    for i = 0, childCount - 1 do
        local child = UIRoot:GetChild(i).gameObject
        if child.name ~= 'Camera' and child.name ~= 'Loading' then
            GameObject.Destroy(child)
        end
    end

    isDisposed = true
end

function CloseCurViewOrQuitGame()
    for i = #allVisibleViews, 1, -1 do
        local view = allVisibleViews[i]
        if not view.visible then
            table.remove(allVisibleViews, i)
        elseif view.View:ReceiveBack() then
            if view.View:OnBack() then
                table.remove(allVisibleViews, i)
                hideView(view.viewName)
            end
            break
        end
    end
end